package com.ubb.dbms.core.checker;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import org.json.simple.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.MongoClient;
import com.ubb.dbms.core.Utils.SQLExceptions.ItemNotExistsException;
import com.ubb.dbms.core.connection.ServerHandler;
import com.ubb.dbms.core.connection.result.MessageResult;
import com.ubb.dbms.core.fileManagers.dataFileManager.DataFileManager;
import com.ubb.dbms.core.fileManagers.indexFileManager.IndexFileIO;
import com.ubb.dbms.core.fileManagers.indexFileManager.IndexFileManager;
import com.ubb.dbms.core.fileManagers.indexFileManager.IndexFileManagerImpl;
import com.ubb.dbms.core.fileManagers.indexFileManager.bPlusTree.BPlusTree;
import com.ubb.dbms.core.fileManagers.indexFileManager.bPlusTree.Node;
import com.ubb.dbms.core.parser.visitors.SQLStatementVisitor;
import com.ubb.dbms.core.systemCatalog.SystemCatalog;
import com.ubb.dbms.domain.queryData.TableRowElements;
import com.ubb.dbms.domain.xml.Database;
import com.ubb.dbms.domain.xml.IAttribute;
import com.ubb.dbms.domain.xml.IndexAttributes;
import com.ubb.dbms.domain.xml.IndexFile;
import com.ubb.dbms.domain.xml.IndexFiles;
import com.ubb.dbms.domain.xml.Table;
import com.ubb.dbms.domain.xml.TableAttribute;

public class DefaultChecker implements DomainObjectChecker {
	private final Logger log = LoggerFactory.getLogger(SQLStatementVisitor.class); 
	private SystemCatalog systemCatalog;

	public DefaultChecker(SystemCatalog systemCatalog) {
		this.systemCatalog = systemCatalog;
	}

	@Override
	public boolean checkDatabase(Database database) {
		if (database.getDatabaseName().trim().isEmpty()) {
			MessageResult.sendMessage(MessageResult.MESSAGE_ERROR, "Empty database name!");
		}
		else {
			try {
				systemCatalog.getDatabase(database.getDatabaseName());
				MessageResult.sendMessage(MessageResult.MESSAGE_INFO, "Database " + database.getDatabaseName() + " already exists.");
				return false; 
			} catch (ItemNotExistsException e) {
				return true;
			}
		}
		return false;
	}
	
	@Override
	public boolean checkUseDatabase(String databaseName) {
		try {
			systemCatalog.getDatabase(databaseName);
			return true;
		} catch (ItemNotExistsException e) {	
			MessageResult.sendMessage(MessageResult.MESSAGE_ERROR, "No database selected!");
			return false;
		}
	}

	@Override
	public boolean checkTable(Table table, String usesDatabase) {
		try {	//duplicate table
			if (usesDatabase == null) {
				MessageResult.sendMessage(MessageResult.MESSAGE_ERROR, "Database not exist.");
				return false;
			}
			
			if (table.getPrimaryKey().getPkAttribute().size() > 1) { 
				MessageResult.sendMessage(MessageResult.MESSAGE_ERROR, "Multiple primary key!");
				return false;
			}
			
			systemCatalog.getTable(table.getTableName(), usesDatabase);
			MessageResult.sendMessage(MessageResult.MESSAGE_INFO,"Table " + table.getTableName() + " already exists.");
			
			return false;
		} catch (ItemNotExistsException e) {
			completeTable(table);
			return true;
		}
	}
	
	private void completeTable(Table table) {
		int rowLength = 0;
		List<TableAttribute> tableAttributes = table.getStructure().getAttributeList();
		for (TableAttribute tableAttribute : tableAttributes) {
			if (tableAttribute.getIsNull() == null) {
				tableAttribute.setIsNull(DomainObjectChecker.DEFAULT_ISNULL);
			}
			rowLength += getAttributeLength(tableAttribute);
		}
		
		table.setRowLenght(Integer.toString(rowLength));
	}
	
	@Override
	public boolean checkRowElements(TableRowElements rowElements, Table table) {
		for (TableAttribute attribute : table.getStructure().getAttributeList()) {
			Object value = rowElements.getColumnValue(attribute.getAttributeName());
			
			if (attribute.getType().equals("int")) {
				try {
					Integer.parseInt((String) value);
				} catch (NumberFormatException e) {
					MessageResult.sendMessage(MessageResult.MESSAGE_INFO,value + " isn't valid number.");
					return false;
				}
			}
			
			if (attribute.getType().equals("varchar")) {
				int lengthValue = value.toString().length();
				if ((((String) value).charAt(0) == '\'') && (((String) value).charAt(((String) value).length() - 1) == '\'') 
						&& (Integer.parseInt(attribute.getLength()) >= lengthValue)) {
					value = ((String) value).substring(1, ((String) value).length() - 1);
					rowElements.setColumnValue(attribute.getAttributeName(), value);
				}
				else {
					MessageResult.sendMessage(MessageResult.MESSAGE_INFO,value + " isn't valid string or long string.");
					return false;
				}
			}
		}
		return true;
	}
	
	private int getAttributeLength(TableAttribute tableAttribute) {
		if (tableAttribute.getLength() != null) {
			try {
				return Integer.parseInt(tableAttribute.getLength());
			} catch (NumberFormatException e) {
				MessageResult.sendMessage(MessageResult.MESSAGE_INFO,"Number format exception in DefaultChecker getAttributeLength. Number = '" + tableAttribute.getLength() + "'. ");
			}
		}
		
		if ("varchar".equalsIgnoreCase(tableAttribute.getType())) {
			tableAttribute.setLength(String.valueOf(DomainObjectChecker.DEFAULT_STRING_LENGTH));
			return DomainObjectChecker.DEFAULT_STRING_LENGTH;
		} if ("int".equalsIgnoreCase(tableAttribute.getType())) {
			tableAttribute.setLength(String.valueOf(DomainObjectChecker.DEFAULT_INT_LENGTH));
			return DomainObjectChecker.DEFAULT_INT_LENGTH;
		}

		return 0;
	}

	@Override
	public void extendWithIndexFile(Table table) {
		IndexFiles indexFiles = new IndexFiles();
		indexFiles.setIndexFileList(new ArrayList<IndexFile>());
		
		IndexFile indexFile = new IndexFile();
		String primaryKey = table.getPrimaryKey().getPkAttribute().get(0).getValue();
		indexFile.setIndexName(primaryKey);
		indexFile.setKeyLenght("4");
		indexFile.setIndexType(IndexFile.B_PLUS_TREE_INDEX_TYPE);
		indexFile.setIsUnique("1");
		IAttribute attribute = new IAttribute();
		attribute.setValue(primaryKey);
		
		IndexAttributes indexAttributes = new IndexAttributes();
		indexAttributes.setIndexAttributeList(new ArrayList<IAttribute>());
		indexAttributes.getIndexAttributeList().add(attribute);
		indexFile.setIndexAttribute(indexAttributes);
		
		indexFiles.getIndexFileList().add(indexFile);
		table.setIndexFiles(indexFiles);
	}

	@Override
	public boolean checkDuplicateKey(TableRowElements rowElements, Table table, String usesDatabase) {
		if (usesDatabase == null) {
			log.error("No database selected!");
			MessageResult.sendMessage(MessageResult.MESSAGE_INFO,"No database selected!");
			return false;
		}
		String primaryKeyColumn = table.getPrimaryKey().getPkAttribute().get(0).getValue();
		
		BasicDBObject whereQuery = new BasicDBObject();
		whereQuery.put("_id", (String)rowElements.getColumnValue(primaryKeyColumn));
		
		MongoClient mongo = ServerHandler.getMongoDB();
		DB db = mongo.getDB(usesDatabase);				
		DBCollection collection = db.getCollection(primaryKeyColumn+"_"+table.getTableName());
		
		DBCursor cursor = collection.find(whereQuery);
		/*whereQuery = new BasicDBObject();
		whereQuery.put("_id", Pattern.compile("^"+(String)rowElements.getColumnValue(primaryKeyColumn)+"#.*"));
		cursor = collection.find(whereQuery);
		System.out.println(cursor.next().toString());*/
		return cursor.size()==0;
		/*for (IndexFile indexFile:table.getIndexFiles().getIndexFileList()) { 
			keyColumnName = indexFile.getIndexAttribute().getIndexAttributeList().get(0).getValue();
			if (primaryKeyColumn.equals(keyColumnName)) {
				indexFileFolder = IndexFileManagerImpl.getIndexFileFolder(usesDatabase, table.getTableName());
				rsIndexFilePath = IndexFileManagerImpl.getRsIndexFilePath(indexFileFolder, indexFile);
			}
		}
		
		IndexFileIO indexFileIO = new IndexFileIO(indexFilePath, rsIndexFilePath, IndexFileManager.HEADER_LENGTH, IndexFileManagerImpl.getIndexRowLength());
		int rootNodeRowNr = indexFileIO.readFileHeader();
		BPlusTree bPlusTree;
		if (rootNodeRowNr == 0) {
			bPlusTree = new BPlusTree(indexFileIO);
		} else {
			Node rootNode = indexFileIO.readRow(rootNodeRowNr);
			bPlusTree = new BPlusTree(rootNode, indexFileIO);
		}
	
		boolean result = bPlusTree.search(Integer.parseInt((String)rowElements.getColumnValue(primaryKeyColumn))) == null;
		if (!result) {
			log.error("Key already exists.");
			MessageResult.sendMessage(MessageResult.MESSAGE_ERROR,"Key already exists.");
		}
		return result;*/
	}
	
}
